package ru.susu.algebra.chartable.constructor;

import java.math.BigDecimal;
import java.math.BigInteger;

import ru.susu.algebra.chartable.IPermutationsGroupCharTable;
import ru.susu.algebra.chartable.PermGroupMapBasedCharTable;
import ru.susu.algebra.field.quadratic.QuadraticFieldGeneralForm;
import ru.susu.algebra.field.quadratic.QuadraticInteger;
import ru.susu.algebra.matrix.Matrix;
import ru.susu.algebra.partition.Partition;
import ru.susu.algebra.partition.PartitionsFacade;
import ru.susu.algebra.partition.filter.AlternatingPartitionsFilter;
import ru.susu.algebra.partition.filter.IPartitionFilter;
import ru.susu.algebra.ui.field.SimpleLegendField;

/**
 * @author akargapolov
 * @since: 09.03.2009
 */
public class AlternatingGroupCharTableConstructor extends PermGroupTableConstructorBase<Matrix>
{
	private static final AlternatingGroupCharTableConstructor _instance = new AlternatingGroupCharTableConstructor();

	private AlternatingGroupCharTableConstructor()
	{
	}

	public static AlternatingGroupCharTableConstructor getInstance()
	{
		return _instance;
	}

	private static final BigInteger TWO = BigInteger.valueOf(2);

	@Override
	public Matrix calcValue(Partition p1, Partition p2) throws Exception
	{
		IPartitionFilter filter = new AlternatingPartitionsFilter();
		if (!filter.checkPartition(p2))
			throw new Exception("Partition " + p2 + " not alternating");

		SymmetricGroupMapTableConstructor symmConstructor = SymmetricGroupMapTableConstructor.getInstance();
		BigInteger value = symmConstructor.getValue(p1, p2);

		boolean specialRow = AlternatingCharTableUtil.isSpecialRow(p1);
		boolean specialColumn = AlternatingCharTableUtil.isSpecialColumn(p2);

		Object valueD2;
		if (value.remainder(TWO).equals(BigInteger.valueOf(0)))
		{
			valueD2 = value.divide(TWO);
		}
		else
		{
			valueD2 = new BigDecimal(value).divide(BigDecimal.valueOf(2.0));
		}

		Matrix res;

		if (specialRow)
		{
			if (specialColumn)
			{
				if (PartitionsFacade.isAlternatingSqrtCharacter(p1, p2))
				{
					res = new Matrix(2, 2);
					BigInteger a = value;
					BigInteger b = BigInteger.ONE;
					BigInteger d = PartitionsFacade.getMainHooksProduct(p1).multiply(value);
					Object value1;
					Object value2;
					QuadraticInteger qInteger1 = new QuadraticInteger(a, b, d);
					QuadraticInteger qInteger2 = new QuadraticInteger(a, b.multiply(BigInteger.valueOf(-1)), d);
					if (qInteger1.getD().equals(BigInteger.valueOf(0)))
					{
						if (!qInteger1.getA().remainder(TWO).equals(BigInteger.ZERO) || !qInteger2.getA().remainder(TWO).equals(BigInteger.ZERO))
						{
							throw new RuntimeException("I can't divide by 2");
						}
						value1 = qInteger1.getA().divide(TWO);
						value2 = qInteger2.getA().divide(TWO);
						//проверено на примере А9, поведение верно
					}
					else
					{
						if (QuadraticFieldGeneralForm.isSpecialCase(qInteger1.getD()))
						{
							value1 = getGeneralForm(qInteger1);
							value2 = getGeneralForm(qInteger2);
						}
						else
						{
							if (!qInteger1.getA().remainder(TWO).equals(BigInteger.ZERO) ||
									!qInteger1.getB().remainder(TWO).equals(BigInteger.ZERO))
							{
								throw new RuntimeException("I can't divide by 2");
							}
							value1 = new QuadraticInteger(qInteger1.getA().divide(TWO), qInteger1.getB().divide(TWO), qInteger1.getD());
							value2 = new QuadraticInteger(qInteger2.getA().divide(TWO), qInteger2.getB().divide(TWO), qInteger2.getD());
						}
						/*
						if (qInteger1.getA().remainder(TWO).equals(BigInteger.ZERO) &&
							qInteger1.getB().remainder(TWO).equals(BigInteger.ZERO))
						{
							value1 = new QuadraticInteger(qInteger1.getA().divide(TWO), qInteger1.getB().divide(TWO), qInteger1.getD());
							value2 = new QuadraticInteger(qInteger2.getA().divide(TWO), qInteger2.getB().divide(TWO), qInteger2.getD());
						}
						else
						{
							value1 = "(" + qInteger1.toString() + ") / 2";
							value2 = "(" + qInteger2.toString() + ") / 2";
						}*/
						String identifier = "P" + p1.getOrderNumber() + "x" + "P" + p2.getOrderNumber();
						value1 = new SimpleLegendField<Object>(identifier, value1);
						value2 = new SimpleLegendField<Object>("/" + identifier, value2);
					}
					res.setValue(0, 0, value1);
					res.setValue(1, 1, value1);
					res.setValue(0, 1, value2);
					res.setValue(1, 0, value2);
				}
				else
				{
					res = new Matrix(2, 2);
					res.setValue(0, 0, valueD2);
					res.setValue(0, 1, valueD2);
					res.setValue(1, 0, valueD2);
					res.setValue(1, 1, valueD2);
				}
			}
			else
			{
				res = new Matrix(2, 1);
				res.setValue(0, 0, valueD2);
				res.setValue(1, 0, valueD2);
			}
		}
		else if (specialColumn)
		{
			res = new Matrix(1, 2);
			res.setValue(0, 0, value);
			res.setValue(0, 1, value);
		}
		else
		{
			res = new Matrix(1, 1);
			res.setValue(0, 0, value);
		}
		return res;
	}

	@Override
	protected IPermutationsGroupCharTable<Matrix> createTable(Partition p)
	{
		return new PermGroupMapBasedCharTable<Matrix>();
	}

	protected Object getGeneralForm(QuadraticInteger source)
	{
		/*
		if (source.getD().compareTo(BigInteger.ZERO) < 0)
		{
			return "(" + source + ") / 2";
		}*/
		BigInteger newA = source.getA().subtract(source.getB());
		if (newA.remainder(TWO).intValue() != 0)
		{
			throw new RuntimeException("I can't divide by 2");
		}
		return new QuadraticFieldGeneralForm(newA.divide(TWO), source.getB(), source.getD());
	}
}
